<template>
  <div
    v-show="visible"
    class="scrolltop"
    @click="scrollTop"
  >
    <em-icon name="shape-nav-up" />
  </div>
</template>

<script>
export default {
  name: 'ScrollTop',
  props: {
    offset: {
      type: Number,
      default: 400
    },
    to: {
      type: Number,
      default: 0
    },
    transitionName: {
      type: String,
      default: 'fade'
    },
    duration: {
      type: Number,
      default: 100
    }
  },
  data() {
    return {
      visible: false,
      isMoving: false
    }
  },
  watch: {
    visible(v) {
      if (v) {
        document
          .getElementsByTagName('body')[0]
          .setAttribute('data-scrolltop', 'on')
      } else {
        document.body.removeAttribute('data-scrolltop')
      }
    }
  },
  mounted() {
    window.addEventListener('scroll', this.handleScroll)
  },
  beforeDestroy() {
    window.removeEventListener('scroll', this.handleScroll)
    document.body.removeAttribute('data-scrolltop')
  },
  methods: {
    handleScroll() {
      this.visible = window.pageYOffset > this.offset
    },
    scrollTop() {
      if (this.isMoving) return
      this.isMoving = true
      this.scrollTo(window.pageYOffset, this.duration, () => {
        this.isMoving = false
      })
    },
    scrollTo(offset, duration, done) {
      duration = duration || 500
      var targetPos = this.to || 0
      var scrollPos =
        window.pageYOffset ||
        document.documentElement.scrollTop ||
        document.body.scrollTop ||
        0
      var from, to

      if (offset) {
        scrollPos += offset
      }

      from = scrollPos
      to = targetPos

      this.animate(
        from,
        to,
        duration,
        value => {
          document.documentElement.scrollTop = value
          document.body.parentNode.scrollTop = value
          document.body.scrollTop = value
        },
        null,
        done
      )
    },

    easings() {},

    animate(from, to, duration, update, easing, done) {
      // Early bail out if called incorrectly
      if (
        typeof from !== 'number' ||
        typeof to !== 'number' ||
        typeof duration !== 'number' ||
        typeof update !== 'function'
      ) {
        return
      }

      // Create mock done() function if necessary
      if (!easing || typeof easing !== 'function') {
        easing = function(t, b, c, d) {
          return (c * t) / d + b
        }
      }

      // Create mock done() function if necessary
      if (typeof done !== 'function') {
        done = function() {}
      }

      // Pick implementation (requestAnimationFrame | setTimeout)
      var rAF =
        window.requestAnimationFrame ||
        function(callback) {
          window.setTimeout(callback, 1000 / 50)
        }

      // Animation loop
      var change = to - from

      function loop(timestamp) {
        var time = (timestamp || +new Date()) - start

        if (time >= 0) {
          update(easing(time, from, change, duration))
        }
        if (time >= 0 && time >= duration) {
          update(to)
          done()
        } else {
          rAF(loop)
        }
      }

      update(from)

      // Start animation loop
      var start =
        window.performance && window.performance.now
          ? window.performance.now()
          : +new Date()

      rAF(loop)
    }
  }
}
</script>

<style scoped>
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.5s;
}

.fade-enter,
.fade-leave-to {
  opacity: 0;
}
</style>
